# -*- coding: utf-8 -*-
"""
彗星亮度预测工具 v7.4
- 支持彗星和小行星
- 根据选择自动调整输入字段
- 修复输入错误
- 新增数据自动识别功能
- 新增导出数据到CSV功能
"""
import threading
import re
import csv
import os
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from tkinter import ttk, messagebox, scrolledtext, filedialog
import tkinter as tk
from datetime import datetime, timedelta
import matplotlib.pyplot as plt
import numpy as np
import matplotlib
matplotlib.use('TkAgg')

# ---------- 中文显示 ----------
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False


# ---------- 主程序 ----------
class CometBrightnessApp:
    def __init__(self, root: tk.Tk):
        self.root = root
        self.root.title("彗星亮度预测工具 v7.4")
        self.root.geometry("1400x900")
        self.root.minsize(1200, 700)

        # 数据缓存
        self.current_dates = None
        self.cobs_calc_m_pred = None
        self.cobs_obs_m_pred = None
        self.astro_m_pred = None
        self.mpc_m_pred = None
        self.r_pred = None
        self.delta_pred = None

        # 子窗口引用
        self.brightness_window = None
        self.distance_window = None

        # 进度条变量
        self.progress_var = tk.IntVar(value=0)
        self.progress_text = tk.StringVar(value="就绪")

        self._build_ui()
        self.root.after(100, self.fill_sample_data)

    # -------------------- UI 构建 --------------------
    def _build_ui(self):
        main_paned = ttk.PanedWindow(self.root, orient=tk.HORIZONTAL)
        main_paned.pack(fill=tk.BOTH, expand=True)

        left_frame = ttk.Frame(main_paned, width=450)
        main_paned.add(left_frame, weight=0)

        right_frame = ttk.Frame(main_paned)
        main_paned.add(right_frame, weight=1)

        # 左侧：参数 + 控制
        self._build_left(left_frame)

        # 右侧：结果 + 进度
        self._build_right(right_frame)

    def _build_left(self, master):
        notebook = ttk.Notebook(master)
        notebook.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

        basic = ttk.Frame(notebook)
        notebook.add(basic, text="基本参数")
        self._build_basic_page(basic)

        model = ttk.Frame(notebook)
        notebook.add(model, text="模型参数")
        self._build_model_page(model)

        ctrl = ttk.Frame(notebook)
        notebook.add(ctrl, text="预测控制")
        self._build_control_page(ctrl)

        # 新增：数据导入页面
        import_frame = ttk.Frame(notebook)
        notebook.add(import_frame, text="数据导入")
        self._build_import_page(import_frame)

    def _build_basic_page(self, master):
        # 彗星/小行星选择
        comet_frame = ttk.LabelFrame(master, text="天体选择", padding=10)
        comet_frame.pack(fill=tk.X, pady=5)
        ttk.Label(comet_frame, text="选择天体:").grid(
            row=0, column=0, sticky=tk.W, padx=5)
        self.comet_type_var = tk.StringVar()
        cb = ttk.Combobox(
            comet_frame, textvariable=self.comet_type_var, width=25, state="readonly")
        cb["values"] = ["彗星", "小行星"]
        cb.grid(row=0, column=1, padx=5)
        cb.bind("<<ComboboxSelected>>", self.on_comet_type_selected)

        # 彗星信息
        info_frame = ttk.LabelFrame(master, text="天体信息", padding=10)
        info_frame.pack(fill=tk.X, pady=5)
        ttk.Label(info_frame, text="天体名称:").grid(
            row=0, column=0, sticky=tk.W, padx=5)
        self.name_entry = ttk.Entry(info_frame, width=25)
        self.name_entry.grid(row=0, column=1, padx=5)

        # 轨道元素
        orbit_frame = ttk.LabelFrame(master, text="轨道元素", padding=10)
        orbit_frame.pack(fill=tk.X, pady=5)
        labels = [
            ("q (近日点距离 AU)", "perihelion_distance"),
            ("a (半长轴 AU)", "semi_major_axis"),
            ("e (偏心率)", "eccentricity"),
            ("ω (近日点幅角 °)", "arg_perihelion"),
            ("Ω (升交点赤经 °)", "long_asc_node"),
            ("i (轨道倾角 °)", "inclination"),
            ("P (轨道周期 年)", "orbital_period"),
            ("T (近日点时间)", "perihelion_time"),
            ("历元时间", "epoch_time")
        ]
        self.orbit_entries = {}
        for idx, (txt, key) in enumerate(labels):
            ttk.Label(orbit_frame, text=txt).grid(
                row=idx, column=0, sticky=tk.W, padx=5)
            ent = ttk.Entry(orbit_frame, width=18)
            ent.grid(row=idx, column=1, padx=5)
            self.orbit_entries[key] = ent

    def _build_model_page(self, master):
        switch = ttk.LabelFrame(master, text="模型开关", padding=10)
        switch.pack(fill=tk.X, pady=5)
        self.use_cobs_calc_var = tk.BooleanVar(value=True)
        self.use_cobs_obs_var = tk.BooleanVar(value=True)
        self.use_astro_var = tk.BooleanVar(value=True)
        self.use_mpc_var = tk.BooleanVar(value=True)
        ttk.Checkbutton(switch, text="启用 MPC 模型",
                        variable=self.use_mpc_var).pack(anchor=tk.W)
        ttk.Checkbutton(switch, text="启用 COBS-Calc 模型",
                        variable=self.use_cobs_calc_var).pack(anchor=tk.W)
        ttk.Checkbutton(switch, text="启用 COBS-Observe 模型",
                        variable=self.use_cobs_obs_var).pack(anchor=tk.W)
        ttk.Checkbutton(switch, text="启用 ASTRO 模型",
                        variable=self.use_astro_var).pack(anchor=tk.W)

        mag = ttk.LabelFrame(master, text="星等参数 (H/n)", padding=10)
        mag.pack(fill=tk.X, pady=5)
        self.cobs_calc_H_entry, self.cobs_calc_n_entry = self._add_hn_row(
            mag, 0, "COBS-Calc")
        self.cobs_obs_H_entry, self.cobs_obs_n_entry = self._add_hn_row(
            mag, 1, "COBS-Observe")
        self.astro_H_entry, self.astro_n_entry = self._add_hn_row(
            mag, 2, "ASTRO")
        self.mpc_H_entry, self.mpc_n_entry = self._add_hn_row(mag, 3, "MPC")

    def _add_hn_row(self, master, row, label):
        ttk.Label(master, text=f"{label} H:").grid(
            row=row, column=0, padx=5, sticky=tk.W)
        h = ttk.Entry(master, width=8)
        h.grid(row=row, column=1, padx=5)
        ttk.Label(master, text="n:").grid(row=row, column=2, padx=5)
        n = ttk.Entry(master, width=8)
        n.grid(row=row, column=3, padx=5)
        return h, n

    def _build_control_page(self, master):
        mode = ttk.LabelFrame(master, text="预测模式", padding=10)
        mode.pack(fill=tk.X, pady=5)
        self.pred_mode = tk.StringVar(value="days")
        ttk.Radiobutton(mode, text="按天数预测", variable=self.pred_mode,
                        value="days").pack(anchor=tk.W)
        ttk.Radiobutton(mode, text="按日期范围预测", variable=self.pred_mode,
                        value="dates").pack(anchor=tk.W)
        self.pred_mode.trace_add("write", self.toggle_pred_mode)

        self.days_frame = ttk.Frame(mode)
        self.days_frame.pack(fill=tk.X, pady=5)
        ttk.Label(self.days_frame, text="预测天数:").pack(side=tk.LEFT, padx=5)
        self.pred_days_entry = ttk.Entry(self.days_frame, width=10)
        self.pred_days_entry.insert(0, "365")
        self.pred_days_entry.pack(side=tk.LEFT, padx=5)

        self.dates_frame = ttk.Frame(mode)
        ttk.Label(self.dates_frame, text="开始日期:").pack(side=tk.LEFT, padx=5)
        self.start_date_entry = ttk.Entry(self.dates_frame, width=12)
        self.start_date_entry.pack(side=tk.LEFT, padx=5)
        ttk.Label(self.dates_frame, text="结束日期:").pack(side=tk.LEFT, padx=5)
        self.end_date_entry = ttk.Entry(self.dates_frame, width=12)
        self.end_date_entry.pack(side=tk.LEFT, padx=5)

        btn = ttk.Frame(master)
        btn.pack(fill=tk.X, pady=10)
        ttk.Button(btn, text="开始预测", command=self.run_prediction).pack(
            side=tk.LEFT, padx=5)
        ttk.Button(btn, text="重置参数", command=self.clear_entries).pack(
            side=tk.LEFT, padx=5)

    def _build_import_page(self, master):
        """构建数据导入页面"""
        import_frame = ttk.LabelFrame(master, text="数据自动识别导入", padding=10)
        import_frame.pack(fill=tk.BOTH, expand=True, pady=5)

        # 说明文本
        help_text = """支持从COBS或MPC网站复制数据自动识别：
        
彗星数据示例 (COBS格式):
q (perihelion distance)    0.504093
a (semi-major axes)        84.678817
e (eccentricity)           0.994047
ω (argument of perihelion) 308.3520
Ω (Longitude of ascending node) 335.3310
i (inclination)            4.4728
P (Orbital period in years) 779.2551
T (time of perihelion passage) 2025-09-12 20:12:11
Epoch 2025 Nov 21
Absolute magnitude         8.9
Slope parameter            5.7

小行星数据示例 (MPC格式):
perihelion distance (AU)   2.0484138
eccentricity               0.1340344
argument of perihelion (°) 279.87569
ascending node (°)         222.30272
inclination (°)            1.84380
perihelion date            2022-03-23.29816
absolute magnitude         18.5
semimajor axis (AU)        2.3654678
period (years)             3.64
epoch                      2024-10-17.0
"""
        help_label = ttk.Label(import_frame, text=help_text, justify=tk.LEFT)
        help_label.pack(fill=tk.X, pady=5)

        # 数据输入文本框
        ttk.Label(import_frame, text="粘贴数据:").pack(anchor=tk.W, pady=(10, 5))
        self.data_text = scrolledtext.ScrolledText(
            import_frame, height=10, width=50)
        self.data_text.pack(fill=tk.BOTH, expand=True, pady=5)

        # 按钮框架
        btn_frame = ttk.Frame(import_frame)
        btn_frame.pack(fill=tk.X, pady=10)

        ttk.Button(btn_frame, text="识别并导入数据",
                   command=self.auto_import_data).pack(side=tk.LEFT, padx=5)
        ttk.Button(btn_frame, text="清空数据",
                   command=self.clear_import_data).pack(side=tk.LEFT, padx=5)
        ttk.Button(btn_frame, text="导入示例数据",
                   command=self.import_sample_data).pack(side=tk.LEFT, padx=5)

    def _build_right(self, master):
        top = ttk.Frame(master)
        top.pack(fill=tk.BOTH, expand=True)

        self._build_result(top)

        bottom = ttk.Frame(master, height=40)
        bottom.pack(fill=tk.X, side=tk.BOTTOM, padx=5, pady=2)
        self.progress_bar = ttk.Progressbar(
            bottom, variable=self.progress_var, maximum=100)
        self.progress_bar.pack(fill=tk.X, side=tk.LEFT,
                               expand=True, padx=(0, 8))
        ttk.Label(bottom, textvariable=self.progress_text,
                  width=20).pack(side=tk.RIGHT)

        chart = ttk.LabelFrame(top, text="图表窗口", padding=10)
        chart.pack(fill=tk.X, pady=5)

        # 新增导出按钮
        export_frame = ttk.Frame(chart)
        export_frame.pack(fill=tk.X, pady=5)
        ttk.Button(export_frame, text="导出数据到CSV",
                   command=self.export_to_csv).pack(side=tk.LEFT, padx=5)

        for txt, cmd in [("打开亮度图表", self.open_brightness_window),
                         ("打开距离图表", self.open_distance_window),
                         ("关闭所有图表", self.close_all_windows)]:
            ttk.Button(chart, text=txt, command=cmd).pack(fill=tk.X, pady=3)

    def _build_result(self, master):
        result = ttk.LabelFrame(master, text="预测结果", padding=10)
        result.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

        canvas = tk.Canvas(result)
        scroll = ttk.Scrollbar(result, orient="vertical", command=canvas.yview)
        scrollable = ttk.Frame(canvas)
        scrollable.bind("<Configure>", lambda e: canvas.configure(
            scrollregion=canvas.bbox("all")))
        canvas.create_window((0, 0), window=scrollable, anchor="nw")
        canvas.configure(yscrollcommand=scroll.set)
        canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scroll.pack(side=tk.RIGHT, fill=tk.Y)

        items = [
            "轨道类型", "近日点日期", "预测总天数", "近地点距离",
            "COBS-Calc最亮星等", "COBS-Calc最亮日期",
            "COBS-Observe最亮星等", "COBS-Observe最亮日期",
            "ASTRO最亮星等", "ASTRO最亮日期",
            "MPC最亮星等", "MPC最亮日期",
            "今日日彗距", "今日地彗距",
            "今日COBS-Calc星等", "今日COBS-Observe星等", "今日ASTRO星等", "今日MPC星等",
            "30天后日彗距", "30天后地彗距", "30天后COBS-Calc星等", "30天后COBS-Observe星等", "30天后ASTRO星等", "30天后MPC星等",
            "60天后日彗距", "60天后地彗距", "60天后COBS-Calc星等", "60天后COBS-Observe星等", "60天后ASTRO星等", "60天后MPC星等",
            "彗发长度 (km)", "彗发长度 (°)", "彗尾长度 (km)", "彗尾长度 (°)"
        ]
        self.info_labels = {}
        for k in items:
            f = ttk.Frame(scrollable)
            f.pack(fill=tk.X, pady=1)
            ttk.Label(f, text=k+":", width=26,
                      anchor="e").pack(side=tk.LEFT, padx=5)
            lab = ttk.Label(f, text="待计算", width=41, anchor="w")
            lab.pack(side=tk.LEFT, padx=5)
            self.info_labels[k] = lab

    # -------------------- 数据导出功能 --------------------
    def export_to_csv(self):
        """导出数据到CSV文件"""
        if self.current_dates is None:
            messagebox.showwarning("警告", "请先进行预测计算再导出数据")
            return

        # 选择保存文件路径
        filename = filedialog.asksaveasfilename(
            title="保存CSV文件",
            defaultextension=".csv",
            filetypes=[("CSV文件", "*.csv"), ("所有文件", "*.*")],
            initialfile=f"{self.name_entry.get()}_亮度预测.csv"
        )

        if not filename:
            return  # 用户取消了保存

        try:
            with open(filename, 'w', newline='', encoding='utf-8-sig') as csvfile:
                writer = csv.writer(csvfile)

                # 写入表头
                headers = ["彗星", "时间", "MPC", "COBS-Calc",
                           "COBS-Observe", "ASTRO", "彗日距", "彗地距"]
                writer.writerow(headers)

                # 写入数据
                for i, date in enumerate(self.current_dates):
                    # 格式化日期为YYYYMMDD
                    date_str = date.strftime("%Y%m%d")

                    # 获取星等数据，如果为None则设为空字符串
                    mpc_mag = f"{self.mpc_m_pred[i]:.2f}" if self.mpc_m_pred is not None else ""
                    cobs_calc_mag = f"{self.cobs_calc_m_pred[i]:.2f}" if self.cobs_calc_m_pred is not None else ""
                    cobs_obs_mag = f"{self.cobs_obs_m_pred[i]:.2f}" if self.cobs_obs_m_pred is not None else ""
                    astro_mag = f"{self.astro_m_pred[i]:.2f}" if self.astro_m_pred is not None else ""

                    # 获取距离数据
                    r = f"{self.r_pred[i]:.7f}" if self.r_pred is not None else ""
                    delta = f"{self.delta_pred[i]:.7f}" if self.delta_pred is not None else ""

                    row = [
                        self.name_entry.get(),
                        date_str,
                        mpc_mag,
                        cobs_calc_mag,
                        cobs_obs_mag,
                        astro_mag,
                        r,
                        delta
                    ]
                    writer.writerow(row)

            messagebox.showinfo("导出成功", f"数据已成功导出到:\n{filename}")

        except Exception as e:
            messagebox.showerror("导出错误", f"导出数据时发生错误:\n{str(e)}")

    # -------------------- 数据导入功能 --------------------
    def auto_import_data(self):
        """自动识别并导入数据"""
        data = self.data_text.get("1.0", tk.END).strip()
        if not data:
            messagebox.showwarning("警告", "请输入要识别的数据")
            return

        try:
            # 先检测小行星数据（更严格的检测）
            if self._detect_and_import_asteroid_data(data):
                messagebox.showinfo("成功", "小行星数据导入成功！")
            # 再检测彗星数据
            elif self._detect_and_import_comet_data(data):
                messagebox.showinfo("成功", "彗星数据导入成功！")
            else:
                messagebox.showerror("错误", "无法识别数据格式，请检查数据格式")
        except Exception as e:
            messagebox.showerror("导入错误", f"数据导入失败: {str(e)}")

    def _detect_and_import_comet_data(self, data):
        """检测并导入彗星数据"""
        lines = data.split('\n')

        # 检测彗星特征关键词
        comet_keywords = ['perihelion distance', 'semi-major axes', 'argument of perihelion',
                          'Longitude of ascending node', 'Absolute magnitude', 'Slope parameter',
                          'Orbital period in years', 'Epoch']
        if not any(keyword in data for keyword in comet_keywords):
            return False

        extracted_data = {}

        for line in lines:
            line = line.strip()
            if not line:
                continue

            # 解析名称
            if 'C/' in line or 'P/' in line or 'SWAN' in line or 'COMET' in line.upper():
                name_match = re.search(
                    r'(C/\d+ [A-Z]+\w*|P/\d+ [A-Z]+\w*|[A-Z]+\d+ [A-Z]+\w*|Comet [A-Z0-9/ ]+)', line, re.IGNORECASE)
                if name_match:
                    extracted_data['name'] = name_match.group(1)

            # 解析轨道参数
            if 'perihelion distance' in line.lower():
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['q'] = match.group(1)
            elif 'semi-major axes' in line.lower() or 'semi-major axis' in line.lower():
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['a'] = match.group(1)
            elif 'eccentricity' in line.lower():
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['e'] = match.group(1)
            elif 'argument of perihelion' in line and '°' not in line:
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['ω'] = match.group(1)
            elif 'longitude of ascending node' in line.lower():
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['Ω'] = match.group(1)
            elif 'inclination' in line.lower() and '°' not in line:
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['i'] = match.group(1)
            elif 'orbital period' in line.lower():
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['P'] = match.group(1)
            elif 'time of perihelion passage' in line.lower():
                # 匹配日期时间格式
                date_match = re.search(r'(\d{4}-\d{2}-\d{2}[\s\d:]+)', line)
                if date_match:
                    extracted_data['T'] = date_match.group(1).strip()
            elif 'epoch' in line.lower() and not line.lower().startswith('epoch jd'):
                # 匹配历元时间
                epoch_match = re.search(
                    r'(\d{4}[-\s]\w+[-\s]\d+|\d{4}-\d{2}-\d{2})', line)
                if epoch_match:
                    extracted_data['epoch'] = epoch_match.group(1)
            elif 'absolute magnitude' in line.lower():
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['H'] = match.group(1)
            elif 'slope parameter' in line.lower():
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['n'] = match.group(1)

        # 填充数据到表单
        if extracted_data:
            self.comet_type_var.set("彗星")
            self.clear_entries()

            if 'name' in extracted_data:
                self.name_entry.delete(0, tk.END)
                self.name_entry.insert(0, extracted_data['name'])

            # 填充轨道参数
            orbit_mapping = {
                'q': 'perihelion_distance',
                'a': 'semi_major_axis',
                'e': 'eccentricity',
                'ω': 'arg_perihelion',
                'Ω': 'long_asc_node',
                'i': 'inclination',
                'P': 'orbital_period',
                'T': 'perihelion_time',
                'epoch': 'epoch_time'
            }

            for key, entry_key in orbit_mapping.items():
                if key in extracted_data:
                    self.orbit_entries[entry_key].delete(0, tk.END)
                    self.orbit_entries[entry_key].insert(
                        0, extracted_data[key])

            # 填充星等参数（彗星使用COBS-Calc）
            if 'H' in extracted_data and 'n' in extracted_data:
                self.cobs_calc_H_entry.delete(0, tk.END)
                self.cobs_calc_H_entry.insert(0, extracted_data['H'])
                self.cobs_calc_n_entry.delete(0, tk.END)
                self.cobs_calc_n_entry.insert(0, extracted_data['n'])

                # 启用COBS-Calc模型
                self.use_cobs_calc_var.set(True)

            return True
        return False

    def _detect_and_import_asteroid_data(self, data):
        """检测并导入小行星数据"""
        lines = data.split('\n')

        # 检测小行星特征关键词 - 更严格的检测
        asteroid_keywords = ['perihelion distance', 'eccentricity', 'argument of perihelion',
                             'ascending node', 'inclination', 'absolute magnitude',
                             'semimajor axis', 'period', 'epoch']

        # 检查是否包含小行星特征且不包含彗星特征
        has_asteroid_features = any(keyword in data.lower()
                                    for keyword in asteroid_keywords)
        has_comet_features = any(keyword in data.lower() for keyword in [
                                 'perihelion passage', 'slope parameter', 'c/', 'p/'])

        if not has_asteroid_features or has_comet_features:
            return False

        extracted_data = {}
        asteroid_detected = False

        for line in lines:
            line = line.strip()
            if not line:
                continue

            # 解析名称（小行星编号）
            if '(' in line and ')' in line and any(char.isdigit() for char in line):
                name_match = re.search(r'\((\d+)\)', line)
                if name_match:
                    extracted_data['name'] = f"小行星 {name_match.group(1)}"
                    asteroid_detected = True

            # 解析轨道参数
            if 'perihelion distance' in line.lower() and 'au' in line.lower():
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['q'] = match.group(1)
                    asteroid_detected = True
            elif 'eccentricity' in line.lower():
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['e'] = match.group(1)
                    asteroid_detected = True
            elif 'argument of perihelion' in line.lower() and '°' in line:
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['ω'] = match.group(1)
                    asteroid_detected = True
            elif 'ascending node' in line.lower() and '°' in line:
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['Ω'] = match.group(1)
                    asteroid_detected = True
            elif 'inclination' in line.lower() and '°' in line:
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['i'] = match.group(1)
                    asteroid_detected = True
            elif 'perihelion date' in line.lower() or 'perihelion date' in line.lower():
                date_match = re.search(r'(\d{4}-\d{2}-\d{2}\.\d+)', line)
                if date_match:
                    extracted_data['T'] = date_match.group(1)
                    asteroid_detected = True
            elif 'semimajor axis' in line.lower() and 'au' in line.lower():
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['a'] = match.group(1)
                    asteroid_detected = True
            elif 'period' in line.lower() and 'years' in line.lower():
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['P'] = match.group(1)
                    asteroid_detected = True
            elif 'epoch' in line.lower() and not line.lower().startswith('epoch jd'):
                epoch_match = re.search(r'(\d{4}-\d{2}-\d{2}\.\d+)', line)
                if epoch_match:
                    extracted_data['epoch'] = epoch_match.group(1)
                    asteroid_detected = True
            elif 'absolute magnitude' in line.lower():
                match = re.search(r'(\d+\.\d+)', line)
                if match:
                    extracted_data['H'] = match.group(1)
                    asteroid_detected = True

        # 填充数据到表单
        if extracted_data and asteroid_detected:
            self.comet_type_var.set("小行星")
            self.clear_entries()

            if 'name' in extracted_data:
                self.name_entry.delete(0, tk.END)
                self.name_entry.insert(0, extracted_data['name'])
            else:
                # 如果没有明确名称，使用默认名称
                self.name_entry.delete(0, tk.END)
                self.name_entry.insert(0, "小行星")

            # 填充轨道参数
            orbit_mapping = {
                'q': 'perihelion_distance',
                'a': 'semi_major_axis',
                'e': 'eccentricity',
                'ω': 'arg_perihelion',
                'Ω': 'long_asc_node',
                'i': 'inclination',
                'P': 'orbital_period',
                'T': 'perihelion_time',
                'epoch': 'epoch_time'
            }

            for key, entry_key in orbit_mapping.items():
                if key in extracted_data:
                    self.orbit_entries[entry_key].delete(0, tk.END)
                    self.orbit_entries[entry_key].insert(
                        0, extracted_data[key])

            # 填充星等参数（小行星使用MPC）
            if 'H' in extracted_data:
                self.mpc_H_entry.delete(0, tk.END)
                self.mpc_H_entry.insert(0, extracted_data['H'])
                self.mpc_n_entry.delete(0, tk.END)
                self.mpc_n_entry.insert(0, "2.0")  # 小行星默认n值

                # 启用MPC模型，禁用其他模型
                self.use_mpc_var.set(True)
                self.use_cobs_calc_var.set(False)
                self.use_cobs_obs_var.set(False)
                self.use_astro_var.set(False)

            return True
        return False

    def clear_import_data(self):
        """清空导入数据文本框"""
        self.data_text.delete("1.0", tk.END)

    def import_sample_data(self):
        """导入示例数据"""
        sample_data = """Comet C/2025 R2 (SWAN)
q (perihelion distance)    0.504093
a (semi-major axes)        84.678817
e (eccentricity)           0.994047
ω (argument of perihelion) 308.3520
Ω (Longitude of ascending node) 335.3310
i (inclination)            4.4728
P (Orbital period in years) 779.2551
T (time of perihelion passage) 2025-09-12 20:12:11
Epoch 2025 Nov 21
Absolute magnitude         8.9
Slope parameter            5.7"""

        self.data_text.delete("1.0", tk.END)
        self.data_text.insert("1.0", sample_data)

    # -------------------- 事件 --------------------
    def toggle_pred_mode(self, *_):
        if self.pred_mode.get() == "days":
            self.days_frame.pack(fill=tk.X, pady=5)
            self.dates_frame.pack_forget()
        else:
            self.days_frame.pack_forget()
            self.dates_frame.pack(fill=tk.X, pady=5)
            if not self.start_date_entry.get():
                self.start_date_entry.insert(
                    0, datetime.now().strftime('%Y-%m-%d'))
            if not self.end_date_entry.get():
                self.end_date_entry.insert(
                    0, (datetime.now() + timedelta(days=1825)).strftime('%Y-%m-%d'))

    def on_comet_type_selected(self, *_):
        self.clear_entries()
        if self.comet_type_var.get() == "彗星":
            self._build_comet_input()
        else:
            self._build_asteroid_input()

    def _build_comet_input(self):
        for key in list(self.orbit_entries.keys())[:-1]:  # Remove 'epoch_time'
            self.orbit_entries[key].delete(0, tk.END)
            self.orbit_entries[key].insert(0, "")

        self.orbit_entries["perihelion_time"].delete(0, tk.END)
        self.orbit_entries["perihelion_time"].insert(0, "2025-11-08 12:54:17")

    def _build_asteroid_input(self):
        for key in list(self.orbit_entries.keys()):
            self.orbit_entries[key].delete(0, tk.END)
            self.orbit_entries[key].insert(0, "")

        self.orbit_entries["perihelion_time"].delete(0, tk.END)
        self.orbit_entries["perihelion_time"].insert(0, "2024-08-04.18975")

    # -------------------- 数据 --------------------
    def fill_sample_data(self):
        self.comet_type_var.set("彗星")
        self.fill_comet_data("323P-B/SOHO")

    def fill_comet_data(self, name):
        data = COMET_SAMPLE_DATA.get(name)
        if not data:
            return
        self.clear_entries()
        self.name_entry.insert(0, data["name"])
        for k, v in data["orbit"].items():
            if v:
                self.orbit_entries[k].delete(0, tk.END)
                self.orbit_entries[k].insert(0, str(v))
        self.use_cobs_calc_var.set(data["model_params"]["use_cobs_calc"])
        self.use_cobs_obs_var.set(data["model_params"]["use_cobs_obs"])
        self.use_astro_var.set(data["model_params"]["use_astro"])
        self.use_mpc_var.set(data["model_params"]["use_mpc"])
        self.cobs_calc_H_entry.insert(
            0, str(data["model_params"]["cobs_calc_H"]))
        self.cobs_calc_n_entry.insert(
            0, str(data["model_params"]["cobs_calc_n"]))
        self.cobs_obs_H_entry.insert(
            0, str(data["model_params"]["cobs_obs_H"]))
        self.cobs_obs_n_entry.insert(
            0, str(data["model_params"]["cobs_obs_n"]))
        self.astro_H_entry.insert(0, str(data["model_params"]["astro_H"]))
        self.astro_n_entry.insert(0, str(data["model_params"]["astro_n"]))
        self.mpc_H_entry.insert(0, str(data["model_params"]["mpc_H"]))
        self.mpc_n_entry.insert(0, str(data["model_params"]["mpc_n"]))

    def clear_entries(self):
        for e in self.orbit_entries.values():
           e.delete(0, tk.END)
        for h, n in [(self.cobs_calc_H_entry, self.cobs_calc_n_entry),
                     (self.cobs_obs_H_entry, self.cobs_obs_n_entry),
                     (self.astro_H_entry, self.astro_n_entry),
                     (self.mpc_H_entry, self.mpc_n_entry)]:
            h.delete(0, tk.END)
            n.delete(0, tk.END)
        for lab in self.info_labels.values():
           lab.config(text="待计算")

    # -------------------- 校验 --------------------
    def validate_inputs(self):
        try:
            name = self.name_entry.get().strip()
            if not name:
                raise ValueError("请输入天体名称")
            q = float(self.orbit_entries["perihelion_distance"].get())
            e = float(self.orbit_entries["eccentricity"].get())
            peri_str = self.orbit_entries["perihelion_time"].get()
            a_str = self.orbit_entries["semi_major_axis"].get()
            a = float(a_str) if a_str else self.calc_semi_major_axis(q, e)

            # 修复日期解析错误
            try:
                if self.comet_type_var.get() == "彗星":
                    peri_date = datetime.strptime(
                        peri_str, "%Y-%m-%d %H:%M:%S")
                else:
                    # 小行星日期格式处理
                    if '.' in peri_str:
                        peri_date = datetime.strptime(
                            peri_str.split('.')[0], "%Y-%m-%d")
                    else:
                        peri_date = datetime.strptime(peri_str, "%Y-%m-%d")
            except ValueError:
                raise ValueError(
                    "近日点时间格式错误，彗星请使用 'YYYY-MM-DD HH:MM:SS'，小行星请使用 'YYYY-MM-DD'")

            # 修复星等参数获取错误
            calc_H = self.cobs_calc_H_entry.get()
            calc_n = self.cobs_calc_n_entry.get()
            obs_H = self.cobs_obs_H_entry.get()
            obs_n = self.cobs_obs_n_entry.get()
            astro_H = self.astro_H_entry.get()
            astro_n = self.astro_n_entry.get()
            mpc_H = self.mpc_H_entry.get()
            mpc_n = self.mpc_n_entry.get()

            if not 0.001 <= q <= 1000:
                raise ValueError("近日点距离应在0.001-1000 AU范围内")
            if e < 0:
                raise ValueError("偏心率不能为负")
            if self.use_cobs_calc_var.get() and calc_H and calc_n:
                if not (-50 <= float(calc_H) <= 5000 and -50 <= float(calc_n) <= 5000):
                    raise ValueError("COBS-Calc模型参数应在-50到5000范围内")
            if self.use_cobs_obs_var.get() and obs_H and obs_n:
                if not (-50 <= float(obs_H) <= 5000 and -50 <= float(obs_n) <= 5000):
                    raise ValueError("COBS-Observe模型参数应在-50到5000范围内")
            if self.use_astro_var.get() and astro_H and astro_n:
                if not (-50 <= float(astro_H) <= 5000 and -50 <= float(astro_n) <= 5000):
                    raise ValueError("ASTRO模型参数应在-50到5000范围内")
            if self.use_mpc_var.get() and mpc_H and mpc_n:
                if not (-50 <= float(mpc_H) <= 5000 and -50 <= float(mpc_n) <= 5000):
                    raise ValueError("MPC模型参数应在-50到5000范围内")
            return (name, q, a, e, peri_date, calc_H, calc_n, obs_H, obs_n, astro_H, astro_n, mpc_H, mpc_n)
        except Exception as e:
            messagebox.showerror("输入错误", str(e))
            return None

    def calc_semi_major_axis(self, q, e):
        if e < 1:
            return q / (1 - e)
        elif e == 1:
            return 1e10  # 使用极大值代表无穷大
        else:
            return q / (1 - e)

    # -------------------- 预测 --------------------
    def run_prediction(self):
        inputs = self.validate_inputs()
        if inputs is None:
            return
        threading.Thread(target=self._do_prediction,
                         args=(inputs,), daemon=True).start()

    def _do_prediction(self, inputs):
        self._set_progress(0, "开始计算……")
        try:
            (name, q, a, e, peri_date, calc_H, calc_n, obs_H,
             obs_n, astro_H, astro_n, mpc_H, mpc_n) = inputs
            if self.pred_mode.get() == "days":
                future_days = int(self.pred_days_entry.get())
                if not 1 <= future_days <= 1_000_000:
                    raise ValueError("预测天数应在1-1,000,000范围内")
                start_date = peri_date - timedelta(days=future_days // 2)
                all_dates = [start_date +
                             timedelta(days=i) for i in range(future_days)]
            else:
                start_date = datetime.strptime(
                    self.start_date_entry.get(), '%Y-%m-%d')
                end_date = datetime.strptime(
                    self.end_date_entry.get(), '%Y-%m-%d')
                delta_days = (end_date - start_date).days
                if delta_days > 1_000_000:
                    raise ValueError("预测范围不能超过1,000,000天")
                all_dates = [start_date +
                             timedelta(days=i) for i in range(delta_days + 1)]
            total = len(all_dates)
            self._set_progress(5, "计算日彗距/地彗距……")

            r_pred = []
            for i, date in enumerate(all_dates):
                days_from_peri = (date - peri_date).days
                if e < 1:
                    M = 2 * np.pi * days_from_peri / (a ** 1.5 * 365.25)
                    E = self.solve_kepler(M, e)
                    r = a * (1 - e * np.cos(E))
                elif e == 1:
                    r = self.calc_parabolic(days_from_peri, q)
                else:
                    r = self.calc_hyperbolic(days_from_peri, q, e, a)
                r_pred.append(max(r, 0.001))
                if i % 100 == 0:
                    self._set_progress(5 + 25 * i // total, "计算日彗距/地彗距……")
            r_pred = np.array(r_pred)

            delta_pred = []
            for i, date in enumerate(all_dates):
                delta = self.calc_earth_distance(date, r_pred[i], e, peri_date)
                delta_pred.append(delta)
                if i % 100 == 0:
                    self._set_progress(30 + 20 * i // total, "计算地彗距……")
            delta_pred = np.array(delta_pred)

            self._set_progress(55, "计算星等……")
            self.cobs_calc_m_pred = None
            self.cobs_obs_m_pred = None
            self.astro_m_pred = None
            self.mpc_m_pred = None
            if self.use_cobs_calc_var.get() and calc_H and calc_n:
                self.cobs_calc_m_pred = (float(
                    calc_H) + 5 * np.log10(delta_pred) + 2.5 * float(calc_n) * np.log10(r_pred))
                self.cobs_calc_m_pred = np.clip(
                    self.cobs_calc_m_pred, -100, 5000)
            if self.use_cobs_obs_var.get() and obs_H and obs_n:
                self.cobs_obs_m_pred = float(
                    obs_H) + 5 * np.log10(delta_pred) + 2.5 * float(obs_n) * np.log10(r_pred)
                self.cobs_obs_m_pred = np.clip(
                    self.cobs_obs_m_pred, -100, 5000)
            if self.use_astro_var.get() and astro_H and astro_n:
                self.astro_m_pred = float(
                    astro_H) + 5 * np.log10(delta_pred) + 2.5 * float(astro_n) * np.log10(r_pred)
                self.astro_m_pred = np.clip(self.astro_m_pred, -100, 5000)
            if self.use_mpc_var.get() and mpc_H and mpc_n:
                self.mpc_m_pred = float(
                    mpc_H) + 5 * np.log10(delta_pred) + 2.5 * float(mpc_n) * np.log10(r_pred)
                self.mpc_m_pred = np.clip(self.mpc_m_pred, -100, 5000)

            self.current_dates = all_dates
            self.r_pred = r_pred
            self.delta_pred = delta_pred
            self._set_progress(90, "更新界面……")
            self.update_results(name, e, all_dates, r_pred, total)
            self.update_brightness_window()
            self.update_distance_window()
            self._set_progress(100, "完成")
            messagebox.showinfo("预测完成", "彗星亮度预测计算完成！ (v7.4)")
        except Exception as e:
            self._set_progress(0, "错误")
            messagebox.showerror("预测错误", str(e))

    def _set_progress(self, val, text):
        self.progress_var.set(val)
        self.progress_text.set(text)
        self.root.update_idletasks()

    # -------------------- 轨道算法 --------------------
    def solve_kepler(self, M, e, tol=1e-8, max_iter=100):
        if abs(M) < 1e-6:
            return M
        E = M if e < 0.8 else (np.pi if M > 0 else -np.pi)
        for _ in range(max_iter):
            delta_E = (E - e * np.sin(E) - M) / (1 - e * np.cos(E))
            E -= delta_E
            if abs(E) > 10 * np.pi:
                E = np.sign(E) * 10 * np.pi
            if abs(delta_E) < tol:
                break
        return E

    def calc_parabolic(self, days_from_peri, q):
        W = 3 * days_from_peri * np.sqrt(0.5 / (q ** 3)) / 2
        s = W
        for _ in range(10):
            s_new = W - s ** 3 / 3
            if abs(s_new - s) < 1e-8:
                break
            s = s_new
        return max(q * (1 + s ** 2), 0.001)

    def calc_hyperbolic(self, days_from_peri, q, e, a):
        N = 2 * np.pi * days_from_peri / ((-a) ** 1.5 * 365.25)
        H = self.solve_hyperbolic_kepler(N, e)
        return max(a * (1 - e * np.cosh(H)), 0.001)

    def solve_hyperbolic_kepler(self, N, e, tol=1e-8, max_iter=100):
        if abs(N) < 0.1:
            H = N
        else:
            H = np.sign(N) * np.log(2 * abs(N) / e + 1.8)
        for _ in range(max_iter):
            sinhH, coshH = np.sinh(H), np.cosh(H)
            f = e * sinhH - H - N
            df = e * coshH - 1
            if abs(df) < 1e-12:
                H += 0.1
                continue
            delta = f / df
            H -= delta
            if abs(H) > 100:
                H = np.sign(H) * 100
            if abs(delta) < tol:
                break
        return H

    def calc_earth_distance(self, date, r, e, peri_date):
        days_from_peri = (date - peri_date).days
        earth_angle = 2 * np.pi * (days_from_peri % 365.25) / 365.25
        earth_x, earth_y = np.cos(earth_angle), np.sin(earth_angle)
        omega = np.radians(float(self.orbit_entries["arg_perihelion"].get()))
        i = np.radians(float(self.orbit_entries["inclination"].get()))
        comet_x = r * np.cos(omega)
        comet_y = r * np.sin(omega) * np.cos(i)
        delta = np.sqrt((comet_x - earth_x) ** 2 + (comet_y - earth_y) ** 2)
        return max(delta, 0.001)

    # -------------------- 结果更新 --------------------
    def update_results(self, name, e, dates, r_pred, total):
        orbit_type = f"椭圆轨道 (e={e:.6f})" if e < 1 else "抛物线轨道 (e=1.000000)" if e == 1 else f"双曲线轨道 (e={e:.6f})"
        self.info_labels["轨道类型"].config(text=orbit_type)
        self.info_labels["预测总天数"].config(text=f"{total:,} 天")

        peri_idx = np.argmin(r_pred)
        peri_date_str = dates[peri_idx].strftime('%Y-%m-%d')
        self.info_labels["近日点日期"].config(text=peri_date_str)
        self.info_labels["近地点距离"].config(text=f"{r_pred[peri_idx]:.7f} AU")

        today = datetime.now().date()
        today_idx = next((i for i, date in enumerate(
            dates) if date.date() == today), None)
        if today_idx is not None:
            today_data = {
                "calc": self.cobs_calc_m_pred[today_idx] if self.cobs_calc_m_pred is not None else "N/A",
                "obs": self.cobs_obs_m_pred[today_idx] if self.cobs_obs_m_pred is not None else "N/A",
                "astro": self.astro_m_pred[today_idx] if self.astro_m_pred is not None else "N/A",
                "mpc": self.mpc_m_pred[today_idx] if self.mpc_m_pred is not None else "N/A",
                "r": r_pred[today_idx],
                "delta": self.delta_pred[today_idx]
            }
            self.info_labels["今日日彗距"].config(text=f"{today_data['r']:.7f}")
            self.info_labels["今日地彗距"].config(
                text=f"{today_data['delta']:.7f} AU")
            for k, lbl in zip(["calc", "obs", "astro", "mpc"], ["今日COBS-Calc星等", "今日COBS-Observe星等", "今日ASTRO星等", "今日MPC星等"]):
                if today_data[k] != "N/A":
                    self.info_labels[lbl].config(text=f"{today_data[k]:.2f}")
        for days in [30, 60]:
            future_date = datetime.now() + timedelta(days=days)
            future_idx = next((i for i, date in enumerate(
                dates) if date.date() == future_date.date()), None)
            if future_idx is not None:
                future_data = {
                    "calc": self.cobs_calc_m_pred[future_idx] if self.cobs_calc_m_pred is not None else "N/A",
                    "obs": self.cobs_obs_m_pred[future_idx] if self.cobs_obs_m_pred is not None else "N/A",
                    "astro": self.astro_m_pred[future_idx] if self.astro_m_pred is not None else "N/A",
                    "mpc": self.mpc_m_pred[future_idx] if self.mpc_m_pred is not None else "N/A",
                    "r": r_pred[future_idx],
                    "delta": self.delta_pred[future_idx]
                }
                self.info_labels[f"{days}天后日彗距"].config(
                    text=f"{future_data['r']:.7f}")
                self.info_labels[f"{days}天后地彗距"].config(
                    text=f"{future_data['delta']:.7f} AU")
                for k, lbl in zip(["calc", "obs", "astro", "mpc"], [f"{days}天后COBS-Calc星等", f"{days}天后COBS-Observe星等", f"{days}天后ASTRO星等", f"{days}天后MPC星等"]):
                    if future_data[k] != "N/A":
                        self.info_labels[lbl].config(
                            text=f"{future_data[k]:.2f}")
        if today_idx is not None:
            coma_length_km, coma_length_deg, tail_length_km, tail_length_deg = self.calc_coma_tail(
                r_pred[today_idx], self.delta_pred[today_idx])
            self.info_labels["彗发长度 (km)"].config(text=f"{coma_length_km:.0f}")
            self.info_labels["彗发长度 (°)"].config(text=f"{coma_length_deg:.4f}")
            self.info_labels["彗尾长度 (km)"].config(text=f"{tail_length_km:.0f}")
            self.info_labels["彗尾长度 (°)"].config(text=f"{tail_length_deg:.4f}")

    def calc_coma_tail(self, r, delta):
        coma_length_km = 100000 / np.sqrt(r)
        tail_length_km = 1000000 / np.sqrt(r)
        delta_km = delta * 149597870.7
        coma_length_deg = np.degrees(coma_length_km / delta_km)
        tail_length_deg = np.degrees(tail_length_km / delta_km)
        return coma_length_km, coma_length_deg, tail_length_km, tail_length_deg

    # -------------------- 图表窗口 --------------------
    def open_brightness_window(self):
        if self.brightness_window is None or not self.brightness_window.winfo_exists():
            self.brightness_window = tk.Toplevel(self.root)
            self.brightness_window.title("亮度预测图表 v7.4")
            self.setup_brightness_window()
            if self.current_dates is not None:
                self.update_brightness_window()
        else:
            self.brightness_window.lift()

    def open_distance_window(self):
        if self.distance_window is None or not self.distance_window.winfo_exists():
            self.distance_window = tk.Toplevel(self.root)
            self.distance_window.title("距离变化图表 v7.4")
            self.setup_distance_window()
            if self.current_dates is not None:
                self.update_distance_window()
        else:
            self.distance_window.lift()

    def close_all_windows(self):
        for w in [self.brightness_window, self.distance_window]:
            if w and w.winfo_exists():
                w.destroy()

    def setup_brightness_window(self):
        if not self.brightness_window.winfo_exists():
            return
        self.brightness_fig, self.brightness_ax = plt.subplots(figsize=(12, 8))
        self.brightness_canvas = FigureCanvasTkAgg(
            self.brightness_fig, master=self.brightness_window)
        self.brightness_canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
        NavigationToolbar2Tk(self.brightness_canvas,
                             self.brightness_window).update()

    def setup_distance_window(self):
        if not self.distance_window.winfo_exists():
            return
        self.distance_fig, self.distance_ax = plt.subplots(figsize=(12, 8))
        self.distance_canvas = FigureCanvasTkAgg(
            self.distance_fig, master=self.distance_window)
        self.distance_canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
        NavigationToolbar2Tk(self.distance_canvas,
                             self.distance_window).update()

    def update_brightness_window(self):
        if (self.brightness_window is None or not self.brightness_window.winfo_exists() or self.current_dates is None):
            return
        self.brightness_ax.clear()
        e = float(self.orbit_entries["eccentricity"].get())
        orbit_type = "椭圆轨道" if e < 1 else ("抛物线轨道" if e == 1 else "双曲线轨道")
        colors = ['#d62728', '#2ca02c', '#000080', '#ff7f0e']
        labels = ['COBS-Calc', 'COBS-Observe', 'ASTRO', 'MPC']
        predictions = [self.cobs_calc_m_pred,
                       self.cobs_obs_m_pred, self.astro_m_pred, self.mpc_m_pred]
        use_models = [self.use_cobs_calc_var.get(), self.use_cobs_obs_var.get(),
                      self.use_astro_var.get(), self.use_mpc_var.get()]
        for i, (pred, color, label, use) in enumerate(zip(predictions, colors, labels, use_models)):
            if pred is not None and use:
                self.brightness_ax.plot(
                    self.current_dates, pred, color=color, label=label, linewidth=2)
                min_mag = min(pred)
                min_idx = np.argmin(pred)
                self.brightness_ax.plot(self.current_dates[min_idx], min_mag, 'o',
                                        color=color, markersize=8,
                                        label=f'{label}最亮: {min_mag:.2f}等')
        today = datetime.now().date()
        today_idx = next((i for i, date in enumerate(
            self.current_dates) if date.date() == today), None)
        if today_idx is not None:
            for i, (pred, color, label, use) in enumerate(zip(predictions, colors, labels, use_models)):
                if pred is not None and use:
                    today_mag = pred[today_idx]
                    self.brightness_ax.plot(self.current_dates[today_idx], today_mag, 's',
                                            color=color, markersize=6,
                                            label=f'今日{label}: {today_mag:.2f}等')
        thirty_days_later = datetime.now() + timedelta(days=30)
        thirty_idx = next((i for i, date in enumerate(
            self.current_dates) if date.date() == thirty_days_later.date()), None)
        if thirty_idx is not None:
            for i, (pred, color, label, use) in enumerate(zip(predictions, colors, labels, use_models)):
                if pred is not None and use:
                    thirty_mag = pred[thirty_idx]
                    self.brightness_ax.plot(self.current_dates[thirty_idx], thirty_mag, 'D',
                                            color=color, markersize=6,
                                            label=f'30天后{label}: {thirty_mag:.2f}等')
        self.brightness_ax.set_ylabel("视星等")
        self.brightness_ax.set_title(
            f"{self.name_entry.get()}亮度预测 - {orbit_type} (v7.4)", fontsize=14)
        self.brightness_ax.grid(True, alpha=0.3)
        self.brightness_ax.legend()
        self.brightness_ax.invert_yaxis()
        self.brightness_fig.autofmt_xdate()
        self.brightness_fig.tight_layout()
        self.brightness_canvas.draw()

    def update_distance_window(self):
        if (self.distance_window is None or not self.distance_window.winfo_exists() or self.current_dates is None):
            return
        self.distance_ax.clear()
        e = float(self.orbit_entries["eccentricity"].get())
        orbit_type = "椭圆轨道" if e < 1 else ("抛物线轨道" if e == 1 else "双曲线轨道")
        self.distance_ax.plot(self.current_dates, self.r_pred,
                              'orange', label='日彗距', linewidth=2)
        self.distance_ax.plot(
            self.current_dates, self.delta_pred, 'blue', label='地彗距', linewidth=2)

        today_idx = next((i for i, date in enumerate(
            self.current_dates) if date.date() == datetime.now().date()), None)
        if today_idx is not None:
            today_r, today_delta = self.r_pred[today_idx], self.delta_pred[today_idx]
            self.distance_ax.plot(self.current_dates[today_idx], today_r, 's', color='purple', markersize=6,
                                  label=f'今日日彗距: {today_r:.7f} AU')
            self.distance_ax.plot(self.current_dates[today_idx], today_delta, 's', color='green', markersize=6,
                                  label=f'今日地彗距: {today_delta:.7f} AU')
        peri_date_str = self.orbit_entries["perihelion_time"].get()
        if peri_date_str:
            try:
                if self.comet_type_var.get() == "彗星":
                    peri_date = datetime.strptime(
                        peri_date_str, "%Y-%m-%d %H:%M:%S")
                else:
                    peri_date = datetime.strptime(
                        peri_date_str.split('.')[0], "%Y-%m-%d")
                peri_idx = next((i for i, date in enumerate(
                    self.current_dates) if date.date() == peri_date.date()), None)
                if peri_idx is not None:
                    peri_r, peri_delta = self.r_pred[peri_idx], self.delta_pred[peri_idx]
                    self.distance_ax.plot(self.current_dates[peri_idx], peri_r, 'D', color='orange', markersize=6,
                                          label=f'近日点: {peri_r:.7f} AU')
                    self.distance_ax.plot(self.current_dates[peri_idx], peri_delta, 'D', color='cyan', markersize=6,
                                          label=f'近日点地彗距: {peri_delta:.7f} AU')
            except ValueError:
                pass
        self.distance_ax.set_ylabel("距离 (AU)")
        self.distance_ax.set_xlabel("日期")
        self.distance_ax.set_title(
            f"{self.name_entry.get()}距离变化 - {orbit_type} (v7.4)", fontsize=14)
        self.distance_ax.grid(True, alpha=0.3)
        self.distance_ax.legend()

        self.distance_fig.autofmt_xdate()
        self.distance_fig.tight_layout()
        self.distance_canvas.draw()


# -------------------- 样本数据 --------------------
COMET_SAMPLE_DATA = {
    "323P-B/SOHO": {
        "name": "323P-B/SOHO",
        "orbit": {
            "perihelion_distance": "0.040063",
            "semi_major_axis": "2.890131",
            "eccentricity": "0.986138",
            "arg_perihelion": "353.9691",
            "long_asc_node": "323.4650",
            "inclination": "5.4627",
            "orbital_period": "4.9135",
            "perihelion_time": "2025-12-16 05:37:06",
            "epoch_time": "2025-10-06"
        },
        "model_params": {
            "use_cobs_calc": True,
            "use_cobs_obs": True,
            "use_astro": True,
            "use_mpc": True,
            "cobs_calc_H": "20.0",
            "cobs_calc_n": "10.0",
            "cobs_obs_H": "19.5",
            "cobs_obs_n": "9.5",
            "astro_H": "18.5",
            "astro_n": "9.0",
            "mpc_H": "21.0",
            "mpc_n": "5.0"
        }
    },
    # 其他彗星数据...
    "2024-08-04.18975": {
        "name": "小行星示例",
        "orbit": {
            "perihelion_distance": "1.9399403",
            "semi_major_axis": "2.3698099",
            "eccentricity": "0.1813941",
            "arg_perihelion": "125.12058",
            "long_asc_node": "267.64434",
            "inclination": "2.93273",
            "orbital_period": "3.65",
            "perihelion_time": "2024-08-04.18975",
            "epoch_time": "2025-05-05.0"
        },
        "model_params": {
            "use_cobs_calc": False,
            "use_cobs_obs": False,
            "use_astro": False,
            "use_mpc": True,
            "cobs_calc_H": "",
            "cobs_calc_n": "",
            "cobs_obs_H": "",
            "cobs_obs_n": "",
            "astro_H": "",
            "astro_n": "",
            "mpc_H": "15.0",
            "mpc_n": "2.0"
        }
    }
}


# -------------------- 启动 --------------------
if __name__ == "__main__":
    root = tk.Tk()
    app = CometBrightnessApp(root)
    root.mainloop()
